Skip to content

Conversation

asgeija
Copy link
Member

@asgeija asgeija commented Oct 12, 2026

Description

Closes #35

What

The detail page now fetches a sighting by id through Apollo Client (SIGHTING_QUERY and useSighting) instead of reading the mock data.

It shows a loading state, an error state with a "Try again" button, and a clear "no sighting with id …" message for unknown ids.
Removed the mock sighting store (sighting-store.ts, use-sightings.ts), and SubmitSightingPage no longer adds to it.

The mock sightings moved to tests/ because the tests still use them as fixtures.

Updated AGENTS.md to match.

Testing

npm run lint and npm run test pass.
Detail page tests cover success, unknown id and error with retry.
Manual check in the browser at desktop and mobile widths

The detail page now queries the sighting by id instead of reading
the mock data.  Part of #35
Nothing reads from the store now that the detail page uses GraphQL,
so SubmitSightingPage no longer adds sightings to it. The mock
sightings move to tests/ because the tests still use them as
fixtures.
@asgeija asgeija added feat New feature frontend labels Oct 12, 2026
@asgeija asgeija added this to the Partial submission 2 milestone Oct 12, 2026
@asgeija asgeija requested a review from a team as a code owner October 12, 2026 11:09
@asgeija asgeija linked an issue Oct 12, 2026 that may be closed by this pull request
3 tasks
Copy link
Member

@thortve thortve left a comment •

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job getting the detail page over to GraphQL, and nice that the mock store is finally gone. I read through the code and found some things. The first four I think we should fix before merging, the rest can wait.

Should fix

1. New sightings don't show up in the list anymore
SubmitSightingPage.tsx:97

Since addSighting() is removed, nothing updates the list after you submit. The list is read from the Apollo cache, so when you go back to the results the new sighting isn't there until you reload the page. It worked before this PR. I think we need refetchQueries or a cache update on the mutation. The test that checked this was also deleted, so we should add a new one.

2. Empty shape and comment
SightingDetailPage.tsx:99 and :107

shape and comments can be null now that the data comes from the database. Then the page shows "Shape" with nothing next to it, and an empty paragraph for the comment. We already have formatShape() which returns "Unknown", so we can just use that. The comment can be hidden when it's null.

3. formatDate() can crash the page
format-sighting.ts:6-8

If the date string isn't a valid date, Intl.DateTimeFormat.format() throws and the whole detail page goes blank. I don't know if we actually have rows like that in the database, but a check that returns "Unknown date" is cheap to add.

4. Weird ids show the wrong sighting
use-sighting.ts:9-11, backend/src/db/sightings.ts:134-139

The id from the URL is sent straight to the SQL query. MySQL turns '1abc' into 1, so /#/sightings/1abc should end up showing sighting 1 instead of "not found" (I haven't tried it, just read the code). We should check that the id is only digits before we query.

Smaller things

5. We fetch the sighting again even though we have it
client.ts:17

The list already loads all the fields, so when you click a sighting it's in the cache. Right now we still send a new request and show "Loading sighting…". Apollo has a way to read it from the cache with a read function on Query.sighting. Could be its own issue.

6. Screen readers don't get told when loading is done
SightingDetailPage.tsx:60-76

Only the error has role="alert". The list uses aria-live and aria-busy, we could do the same here.

7. "Try again" button has no styling
SightingDetailPage.tsx:68

The one in the list uses .sighting-results__button and has a focus outline. This one is just a plain button, so it looks different.

8. Same fields written three times
queries.ts:85-99

SIGHTING_QUERY, SIGHTINGS_QUERY and the create mutation all list the same eleven fields. A shared fragment would mean we only have to change one place when we add a field.

9. retry is copied
use-sighting.ts:13-17

Same code as in use-sighting-search.ts:76-80. Not important, but could be one helper.

Clear the cached sighting lists after a submit so the new sighting
shows up in the search results. Show "Unknown" for a missing shape
and hide empty comments. Return "Unknown date" for invalid dates
instead of throwing. Return not found for ids that are not plain
numbers, since MySQL reads '1abc' as 1.

Part of #35
@asgeija
Copy link
Member Author

asgeija commented Oct 12, 2026 •

Good job getting the detail page over to GraphQL, and nice that the mock store is finally gone. I read through the code and found some things. The first four I think we should fix before merging, the rest can wait.

Should fix

1. New sightings don't show up in the list anymore SubmitSightingPage.tsx:97

Since addSighting() is removed, nothing updates the list after you submit. The list is read from the Apollo cache, so when you go back to the results the new sighting isn't there until you reload the page. It worked before this PR. I think we need refetchQueries or a cache update on the mutation. The test that checked this was also deleted, so we should add a new one.

2. Empty shape and comment SightingDetailPage.tsx:99 and :107

shape and comments can be null now that the data comes from the database. Then the page shows "Shape" with nothing next to it, and an empty paragraph for the comment. We already have formatShape() which returns "Unknown", so we can just use that. The comment can be hidden when it's null.

3. formatDate() can crash the page format-sighting.ts:6-8

If the date string isn't a valid date, Intl.DateTimeFormat.format() throws and the whole detail page goes blank. I don't know if we actually have rows like that in the database, but a check that returns "Unknown date" is cheap to add.

4. Weird ids show the wrong sighting use-sighting.ts:9-11, backend/src/db/sightings.ts:134-139

The id from the URL is sent straight to the SQL query. MySQL turns '1abc' into 1, so /#/sightings/1abc should end up showing sighting 1 instead of "not found" (I haven't tried it, just read the code). We should check that the id is only digits before we query.

Smaller things

5. We fetch the sighting again even though we have it client.ts:17

The list already loads all the fields, so when you click a sighting it's in the cache. Right now we still send a new request and show "Loading sighting…". Apollo has a way to read it from the cache with a read function on Query.sighting. Could be its own issue.

6. Screen readers don't get told when loading is done SightingDetailPage.tsx:60-76

Only the error has role="alert". The list uses aria-live and aria-busy, we could do the same here.

7. "Try again" button has no styling SightingDetailPage.tsx:68

The one in the list uses .sighting-results__button and has a focus outline. This one is just a plain button, so it looks different.

8. Same fields written three times queries.ts:85-99

SIGHTING_QUERY, SIGHTINGS_QUERY and the create mutation all list the same eleven fields. A shared fragment would mean we only have to change one place when we add a field.

9. retry is copied use-sighting.ts:13-17

Same code as in use-sighting-search.ts:76-80. Not important, but could be one helper.

I fixed the first four:
New sightings missing from the list: after submitting, I now clear the cached lists so the search page fetches fresh results. I did it this way because the search page isn't mounted on the submit page, so refetchQueries wouldn't reach it. Added a test.

Null shape and comments: the page now shows "Unknown" for a missing shape and hides the comment when it's null. Added a test.

formatDate(): returns "Unknown date" for invalid dates instead of crashing. Added a test.

Weird ids: the backend now returns not found unless the id is only digits. No test for this one, since we have no database tests, but I checked it by hand.

I'm leaving 5-9 for follow-up issues

Copy link
Member

@jonatsm jonatsm left a comment •

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just one thing that should be addressed:

  1. The error always says "Check your connection" (SightingDetailPage.tsx:66). If the server or the database fails, that's wrong, and retrying doesn't help.

The detail page said "Check your connection" for every error. If
the backend answers with a GraphQL error, such as a database
failure, it now says the server could not load the sighting and
hides the retry button, since retrying won't help.

Network
failures keep the old message and the button.

Part of #35
@asgeija
Copy link
Member Author

asgeija commented Oct 12, 2026

Looks good! Just one thing that should be addressed:

  1. The error always says "Check your connection" (SightingDetailPage.tsx:66). If the server or the database fails, that's wrong, and retrying doesn't help.

Fixed :)

Copy link
Member

@jonatsm jonatsm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff!

@asgeija
Copy link
Member Author

asgeija commented Oct 14, 2026

@thortve Could you take another look at this?

@asgeija asgeija merged commit 2e44bce into main Oct 14, 2026
1 check passed
@asgeija asgeija deleted the 35-connect-the-detail-page-to-the-backen branch October 15, 2026 08:54
Sign in to join this conversation on GitHub.
Labels
feat New feature frontend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Connect the detail page to the backend
3 participants